package summarizer.newversion;

import java.util.ArrayList;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Random;

import thesis.FSModule;
import thesis.InfoUnit;
import thesis.DataObject;
import thesis.Summary;

import com.aliasi.util.Pair;

public class GreedySummarizerItOptNewFilterTotal extends Summarizer {

	private final double alpha;
	private final double beta;
	private final double gamma;
	private final double[][] probs;
	private final ArrayList<InfoUnit> infoUnits;
	private HashMap<Long, DataObject> memoryTweets = new HashMap<Long, DataObject>();
	private double totalInfo;

	public GreedySummarizerItOptNewFilterTotal(final double[][] probs,
			ArrayList<InfoUnit> infoUnits, double alpha, double beta,
			double gamma) {
		this.alpha = alpha;
		this.beta = beta;
		this.gamma = gamma;
		this.infoUnits = infoUnits;
		this.probs = probs;
	}

	private double getTotalC(HashMap<Long, DataObject> tweets) {
		double total = 0;
		for (DataObject t : tweets.values()) {
			for (Pair<Double, InfoUnit> pair : t.getUnits()) {
				total += pair.a() * pair.b().getWeight();
			}
		}
		return total;
	}

	private DataObject preprocess(Summary summary, int summaryDimension,
			int dimSize, LinkedList<DataObject> remainTweets) {
		// initialized totalInfo contained in each tweet
		this.totalInfo = this.getTotalC(memoryTweets);
		for (DataObject t : this.memoryTweets.values()) {
			t.setInS(false);
			t.setQual(t.getQuality() / summaryDimension);
			t.setDiv(1);
		}
		// set gain for each unit
		for (InfoUnit unit : this.infoUnits) {
			unit.setGain(0);
		}
		for (DataObject t : this.memoryTweets.values()) {
			for (Pair<Double, InfoUnit> pair : t.getUnits()) {
				InfoUnit unit = pair.b();
				unit.setGain(unit.getGain() + unit.getWeight() * pair.a());
			}
		}
		for (DataObject t : this.memoryTweets.values()) {
			double cov = 0;
			for (Pair<Double, InfoUnit> pair : t.getUnits()) {
				InfoUnit unit = pair.b();
				double covProb = probs[t.getInternId()][unit.getInternId()];
				double gainT = covProb * unit.getWeight();
				cov += gainT + covProb * (unit.getGain() - gainT);
			}
			t.setOrigCov(cov / this.totalInfo);
			t.setCov(t.getOrigCov());
		}
		// find tweet with best quality
		DataObject bestT = null;
		double bestScore = -1;
		for (DataObject t : memoryTweets.values()) {
			double score = alpha * t.getQual() + gamma * t.getOrigCov();
			t.setUp(score);
			if (score > bestScore) {
				bestScore = score;
				bestT = t;
			}
		}
		summary.addMemoryTweet(bestT);
		this.memoryTweets.remove(bestT.getDbId());
		for (InfoUnit unit : this.infoUnits) {
			unit.setCovProb(probs[bestT.getInternId()][unit.getInternId()]);
			unit.setGain(unit.getGain()
					- probs[bestT.getInternId()][unit.getInternId()]
					* unit.getWeight());
		}
		// add tweets in remainTweets and sort remainTweets
		remainTweets.clear();
		for (DataObject t : memoryTweets.values()) {
			remainTweets.add(t);
		}
		Collections.sort(remainTweets);
		return bestT;
	}

	private void greedProcess(int summaryDimension, int numberOfPages,
			Summary summary, DataObject bestT,
			LinkedList<DataObject> remainTweets) {
		while (summary.size() < summaryDimension) {
			DataObject prevT = bestT;
			double bestS = -1;
			// --for randomize tie--
			ArrayList<DataObject> ties = new ArrayList<DataObject>();
			//----------------------
			for (Iterator<DataObject> it = remainTweets.listIterator(); it
					.hasNext();) {
				DataObject t = it.next();
				if (t.isInS()) {
					continue;
				}
				if (t.getUp() < bestS) {
					break;
				}
				if (alpha * t.getQual() + beta * t.getDiv() + gamma
						* Math.min(t.getOrigCov(), 1 - prevT.getCov()) < bestS) {
					continue;
				}
				double cov = 0;
				for (Pair<Double, InfoUnit> pair : t.getUnits()) {
					InfoUnit unit = pair.b();
					double covProb = probs[t.getInternId()][unit.getInternId()];
					double gainT = covProb * unit.getWeight();
					cov += probs[t.getInternId()][unit.getInternId()]
							* (1 - unit.getCovProb())
							* (unit.getWeight() + unit.getGain() - gainT);
				}
				t.setCov(prevT.getCov() + cov / this.totalInfo);
				t.setOrigCov(t.getCov() - prevT.getCov());
				double dif = alpha * t.getQual() + beta * t.getDiv() + gamma
						* t.getOrigCov();
				if (bestS < dif) {
					bestS = dif;
					bestT = t;
					// --for randomize tie--
					ties.clear();
					ties.add(t);
					//---------------------
				}
				// --for randomize tie--
				if (bestS == dif) {
					ties.add(t);
				}
				//----------------------
			}
			// --for randomize tie--
			int rd =new Random().nextInt(ties.size());
			bestT = ties.get(rd);
			//------------------------
			summary.addMemoryTweet(bestT);
			this.memoryTweets.remove(bestT.getDbId());
			bestT.setInS(true);
			for (Pair<Double, InfoUnit> pair : bestT.getUnits()) {
				InfoUnit info = pair.b();
				info.setCovProb(1 - (1 - info.getCovProb())
						* (1 - probs[bestT.getInternId()][info.getInternId()]));
				double gainBest = probs[bestT.getInternId()][info.getInternId()]
						* info.getWeight();
				info.setGain(info.getGain() - gainBest);
			}

			if (FSModule.DEBUG) {
				System.out.println("Summary size: " + summary.size());
				System.out.println("Quality: " + bestT.getQual()
						+ " Diversity: " + bestT.getDiv() + " Coverage: "
						+ bestT.getCov());
			}

		}
	}

	public Summary computeSummary(int summaryDimension, int numberOfPages,
			HashMap<Long, DataObject> tweets, int dimSize) {
		this.memoryTweets = tweets;
		Summary summary = new Summary();
		if (summaryDimension <= 0 || this.memoryTweets.isEmpty()) {
			return summary;
		}
		executionTime = (new GregorianCalendar()).getTimeInMillis();
		LinkedList<DataObject> remainTweets = new LinkedList<DataObject>();
		DataObject bestT = preprocess(summary, summaryDimension, dimSize,
				remainTweets);
		greedProcess(summaryDimension, numberOfPages, summary, bestT,
				remainTweets);
		executionTime = (new GregorianCalendar()).getTimeInMillis()
				- executionTime;
		return summary;
	}

	public String toString() {
		return "Greedy Summarizer Iteration Level Opt New Filter Total" + "<"
				+ alpha + " " + beta + " " + gamma + ">";
	}

	public long getExecutionTime() {
		return executionTime;
	}
}
